﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;
using BIStudio.Framework;
using BIStudio.Framework.Log.Common.Internal;
using BIStudio.Framework.Utils;
using System.Reflection;

namespace BIStudio.Framework.Log
{
    public class LogService
    {
        #region 私有方法

        //private const string PublicModule = "BIFramework.Remoting";
        private const string PublicModule = "BIFramework.Local";
        public static readonly LogService Default = new LogService();

        private readonly ConcurrentDictionary<string, ILog> _logs;
        private readonly TraceLevel _traceLevel = TraceLevel.Verbose;

        private LogService()
        {
            _logs = new ConcurrentDictionary<string, ILog>();
            string level = string.IsNullOrEmpty(ALConfig.AppSettings["TraceLevel"])
                ? ConfigurationManager.AppSettings["TraceLevel"]
                : "Info";
            if (string.IsNullOrEmpty(level))
                level = _traceLevel.ToString();
            _traceLevel = (TraceLevel) Enum.Parse(typeof (TraceLevel), level);
        }

        private ILog GetLogProvider(string module)
        {
            string key = GetKey(module);
            return _logs.GetOrAdd(key, LogFactory.CreateLog);
        }

        private string GetKey(string module)
        {
            string path =
                ALConfig.DllConfigs.IsExists(module) &&
                ALConfig.DllConfigs[module].IsExists("Log") &&
                ALConfig.DllConfigs[module]["Log"].IsExists("Provider") ?
                ALConfig.DllConfigs[module]["Log"]["Provider"] :
                "Log4";
            return path;
        }

        #endregion

        /// <summary>
        ///     写日志，所有写日志的入口moduleName
        /// </summary>
        /// <param name="info"></param>
        private void WriteLog(STDLog info,string moduleName)
        {
            //if ((int)_traceLevel < info.LogLevel)
            //    return;

            ILog log = GetLogProvider(moduleName);
            log.WriteLog(info);
        }
        /// <summary>
        ///     写调试和跟踪消息
        /// </summary>
        /// <param name="context">操作内容</param>
        /// <param name="moduleName">内容模块</param>
        /// <param name="entityType">对应数据表名</param>
        /// <param name="entityId">对应数据表主键</param>
        /// <param name="entityTitle">内容标题，对应数据标题，可为空</param>
        /// <param name="summary">操作摘要</param>
        /// <param name="operatetype">操作类型，操作类型，CRUD或绑定到权限系统，表示用户点击了指定的按钮</param>
        /// <param name="additional">附加字段</param>
        public void WriteDEBUG(string context, string moduleName = LogService.PublicModule, string entityType = "", long entityId = 0,
            string entityTitle = "", string summary = "", string operatetype = "",
            Dictionary<string, object> additional = null)
        {
            WriteLog(CreateLogInfo(Assembly.GetCallingAssembly().GetName().Name, LogLevelEnum.DEBUG, additional, entityId, entityTitle, entityType,
                context, summary, operatetype), moduleName);
        }
        /// <summary>
        ///     写信息性消息、警告和错误处理消息。
        /// </summary>
        /// <param name="context">操作内容</param>
        /// <param name="moduleName">内容模块</param>
        /// <param name="entityType">对应数据表名</param>
        /// <param name="entityId">对应数据表主键</param>
        /// <param name="entityTitle">内容标题，对应数据标题，可为空</param>
        /// <param name="summary">操作摘要</param>
        /// <param name="operatetype">操作类型，操作类型，CRUD或绑定到权限系统，表示用户点击了指定的按钮</param>
        /// <param name="additional">附加字段</param>
        public void WriteInfo(string context, string moduleName = null, string entityType = "", long entityId = 0,
            string entityTitle = "", string summary = "", string operatetype = "",
            Dictionary<string, object> additional = null)
        {
            WriteLog(CreateLogInfo(Assembly.GetCallingAssembly().GetName().Name, LogLevelEnum.INFO, additional, entityId, entityTitle, entityType, context,
                summary, operatetype), moduleName);
        }

        /// <summary>
        ///     写警告消息
        /// </summary>
        /// <param name="context">操作内容</param>
        /// <param name="moduleName">内容模块</param>
        /// <param name="entityType">对应数据表名</param>
        /// <param name="entityId">对应数据表主键</param>
        /// <param name="entityTitle">内容标题，对应数据标题，可为空</param>
        /// <param name="summary">操作摘要</param>
        /// <param name="operatetype">操作类型，操作类型，CRUD或绑定到权限系统，表示用户点击了指定的按钮</param>
        /// <param name="additional">附加字段</param>
        public void WriteWarning(string context, string moduleName = null, string entityType = "", long entityId = 0,
            string entityTitle = "", string summary = "", string operatetype = "",
            Dictionary<string, object> additional = null)
        {
            WriteLog(CreateLogInfo(Assembly.GetCallingAssembly().GetName().Name, LogLevelEnum.WARN, additional, entityId, entityTitle, entityType,
                context, summary, operatetype), moduleName);
        }

        /// <summary>
        ///     写错误处理消息。
        /// </summary>
        /// <param name="context">操作内容</param>
        /// <param name="moduleName">内容模块</param>
        /// <param name="entityType">对应数据表名</param>
        /// <param name="entityId">对应数据表主键</param>
        /// <param name="entityTitle">内容标题，对应数据标题，可为空</param>
        /// <param name="summary">操作摘要</param>
        /// <param name="operatetype">操作类型，操作类型，CRUD或绑定到权限系统，表示用户点击了指定的按钮</param>
        /// <param name="additional">附加字段</param>
        public void WriteError(string context, string moduleName = null, string entityType = "", long entityId = 0,
            string entityTitle = "", string summary = "", string operatetype = "",
            Dictionary<string, object> additional = null)
        {
            WriteLog(CreateLogInfo(Assembly.GetCallingAssembly().GetName().Name, LogLevelEnum.ERROR, additional, entityId, entityTitle, entityType, context,
                summary, operatetype), moduleName);
        }

        /// <summary>
        ///     读取日志
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public List<STDLog> Read(LogCondtiton condition)
        {
            if (!string.IsNullOrEmpty(condition.EntityModule))
            {
                ILog log = GetLogProvider(condition.EntityModule);
                return condition.LogLevel.HasValue ? log.ReadLogs(condition) : ReadAllLevel(log, condition);
            }
            return _logs.AsParallel().SelectMany(pair =>
            {
                LogCondtiton tmpCondition = condition.ShallowClone();
                ILog log = pair.Value;
                tmpCondition.EntityModule = pair.Key;
                return condition.LogLevel.HasValue ? pair.Value.ReadLogs(condition) : ReadAllLevel(log, condition);
            }).ToList();
        }

        /// <summary>
        ///     读取所有级别的日志
        /// </summary>
        /// <param name="log"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        private List<STDLog> ReadAllLevel(ILog log, LogCondtiton condition)
        {
            return Enum.GetValues(typeof (LogLevelEnum))
                .Cast<int>()
                .SelectMany(item =>
                {
                    condition.LogLevel = item;
                    return log.ReadLogs(condition) ?? new List<STDLog>();
                })
                .ToList();
        }

        private STDLog CreateLogInfo(string moduleName, LogLevelEnum level, Dictionary<string, object> additional,
            long entityId = 0, string entityTitle = "", string entityType = "", string context = "", string summary = "",
            string operatetype = "")
        {
            return new STDLog
            {
                ID = CFID.NewID(),
                EntityID = entityId,
                EntityTitle = entityTitle,
                EntityModule = moduleName,
                EntityType = entityType,
                LogLevel = (int) level,
                OperateContent = context,
                OperateDetail = GetStackTace(),
                OperateSummary = summary,
                OperateType = operatetype,
                UserID = CFContext.User.ID,
                UserIP = CFContext.User.IP,
                UserName = CFContext.User.UserName,
                CreateTime = DateTime.Now,
                Additional = additional ?? new Dictionary<string, object>(),
            };
        }

        /// <summary>
        ///     获取堆栈的内容
        /// </summary>
        /// <returns></returns>
        private string GetStackTace()
        {
            var st = new StackTrace(true);
            var stackInfo = new StringBuilder();
            //var error = HttpContext.Current.Server.GetLastError();
            //if (error != null)
            //{
            //    stackInfo.Append(error.ToString());
            //    return stackInfo.ToString();
            //}
            for (int i = 0; i < st.FrameCount; i++)
            {
                stackInfo.Append("\r\n");
                stackInfo.Append("".PadLeft(i, ' '));
                stackInfo.Append(String.Format("在 {0} 位置 {1}:行号 {2}"
                    , st.GetFrame(i).GetMethod()
                    , st.GetFrame(i).GetFileName()
                    , st.GetFrame(i).GetFileLineNumber()));
            }
            return stackInfo.ToString();
        }
    }
}